package Jona;

import java.util.*;
import java.io.*;
import java.util.Hashtable;
/**
 * @author Xox Ix
 *
 * TODO To change the template for this generated type comment go to
 * Window - Preferences - Java - Code Style - Code Templates
 */
public class Theory implements Serializable {
	protected boolean debugInformation = true;
	protected Hashtable statements;
	
	public Theory() {
		statements = new Hashtable();
	}
	
	/**
	 * Adds a statement to the theory if it is not present.
	 * @param newStatement statement to add to the theory
	 */	
	public void addStatement(ArgObject newStatement)
	{
		if (newStatement != null && !statements.contains(newStatement)) 
		{
			statements.put(newStatement,new Integer(ArgObject.EVALUATION_UNKNOWN));
		}
	}
	
	
	/**
	 * Adds a statement (recursive) to the theory if it is not present.
	 * @param newStatement statement to add to the theory
	 */	
	public void addStatementRecursive(ArgObject newStatement) {
		ArgObject [] tmp;
		int i;
		
		if (!statements.contains(newStatement)) {
			// Prevent any (im)possible kind of looping
			statements.put(newStatement,new Integer(ArgObject.EVALUATION_UNKNOWN));
			if (newStatement instanceof Conjunct) {
				tmp = ((Conjunct)newStatement).getOperands();
				for (i = 0; i < tmp.length; ++i) {
					addStatement(tmp[i]);
				}
			} else if (newStatement instanceof Negation) {
				addStatement(newStatement.getNegation());
			} else if (newStatement instanceof Conditional) {
				addStatement(((Conditional)newStatement).getAntecedent());
				addStatement(((Conditional)newStatement).getConsequent());
			} else if (newStatement instanceof Power) {
				tmp = (((Power)newStatement).getStrong());
				for (i = 0; i < tmp.length; ++i) {
					addStatement(tmp[i]);
				}
				tmp = (((Power)newStatement).getWeak());
				for (i = 0; i < tmp.length; ++i) {
					addStatement(tmp[i]);
				}
			}
		}
	}
	
	/**
	 * Removes a given statement from the theory if it is present
	 * @param oldStatement statement to be removed
	 */
	public void removeStatement(ArgObject oldStatement) {
		statements.remove(oldStatement);
	}
	
	/**
	 * Empties the theory of all statements and evaluations.
	 *
	 */
	public void clear() {
		statements.clear();
	}
	
	/**
	 * Retrieves the statements in the theory
	 * @return an array of statements in the theory
	 */
	public ArgObject[] getStatements() {
		int i;
		Object [] objects = statements.keySet().toArray();
		ArgObject [] retVal = new ArgObject[objects.length];
		for (i = objects.length-1; i >= 0; --i) {
			retVal[i] = (ArgObject)objects[i];
		}
		return retVal;
	}
	
	protected void makePowerRelations() {
		Enumeration keys;
		int i;
		Vector tiebreakers = new Vector();
		Object curObject;
		Power obj1, obj2;
		
		keys = statements.keys();
		while (keys.hasMoreElements()) {
			curObject = keys.nextElement();
			if (curObject instanceof Power) {
				obj1 = (Power)curObject;
				for (i = 0; i < tiebreakers.size(); ++i) {
					obj2 = (Power)tiebreakers.get(i);
					if (obj2.invalidates(obj1) && !obj1.overrules(obj2)) {
						obj1.addAttacker(obj2);
					}  
					if (obj1.invalidates(obj2) && !obj2.overrules(obj1)) {
						obj2.addAttacker(obj1);
					}
				}
				tiebreakers.add(curObject);
			}
		}
		
	}
	
	public void removeAllChilds(Statement s1)
	{
		
	}
	
	public void removeAllChildConditionals(Conditional c1)
	{


		ArgObject[] statements = getStatements(); 	
		
		for(int i =0; i < statements.length ; i++)
		{		
			if(statements[i] instanceof Negation)
			{
				Negation n1 = (Negation)statements[i];
				if(n1.getNegation() instanceof Conditional)
				{
					if(((Conditional)n1.getNegation()).getConsequent() == c1)
					{
						removeAllChildConditionals((Conditional)n1.getNegation());
					}			
				}
			}
			else if(statements[i] instanceof Conditional)
			{
				//System.out.println("found one " + statements[i].getText());
				Conditional c2 = (Conditional)statements[i];
				if(c2.getConsequent() instanceof Negation)
				{
					System.out.println("negation " + statements[i].getText());
					ArgObject ao1 = c2.getConsequent().getNegation();
					if(ao1 instanceof Conditional && ao1 == c1)
					{
						System.out.println("removing negation!");
						removeAllChildConditionals((Conditional)statements[i]);
					}
				}
				else 
				{
					ArgObject ao1 = c2.getConsequent();
					if(ao1 instanceof Conditional && ao1 == c1)
						removeAllChildConditionals((Conditional)statements[i]);
				}
			}
		}
		System.out.println("removing :" + c1.getText());
		this.removeStatement(c1);
	}
	
	/**
	 * Evaluates all statements in the theory
	 *
	 */
	public void evaluate() {
		int i, newVal;
		boolean didChange;
		Enumeration keys;
		ArgObject curStatement;
		
		// Reset all variables to "unknown"
		keys = statements.keys();
		while (keys.hasMoreElements()) {
			curStatement = (ArgObject)keys.nextElement();
			statements.put(curStatement, new Integer(ArgObject.EVALUATION_UNKNOWN));
		}
		
		makePowerRelations();
		
		do {
			// Update unknown evaluations until it fails
			didChange = false;
			keys = statements.keys();
			while (keys.hasMoreElements()) {
				curStatement = (ArgObject)keys.nextElement();
				if (getValue(curStatement) == ArgObject.EVALUATION_UNKNOWN) {
					newVal = curStatement.evaluate(this);
					if (newVal != ArgObject.EVALUATION_UNKNOWN) {
						statements.put(curStatement, new Integer(newVal));
						didChange = true;
					}
				} 
				if (curStatement instanceof Power) {
					if (getValue(curStatement) == ArgObject.EVALUATION_UNDEFEATED) {
						didChange = didChange || ((Power)curStatement).apply(this);
					}  
				}
			}
		} while (didChange);
		
		if (debugInformation) {
			System.out.println("** Evaluation complete");
		}
	}
	
	/**
	 * Evaluates a statement if it is still markes as unknown.
	 * @param st Statement to evaluate
	 * @return
	 * Statement.EVALUATION_UNASSIGNED if the statement was unassigned or not present,
	 * Statement.EVALUATION_UNDEFEATED if the statement is justified,
	 * Statement.EVALUATION_DEFEATED if the statement is defeated,
	 * Statement.EVALUATION_UNKNOWN if there is conflicting evidence
	 */
	protected int evaluate(ArgObject st) {
		int tmpVal;
		tmpVal = getValue(st);
		if (tmpVal == ArgObject.EVALUATION_UNKNOWN) {
			tmpVal = st.evaluate(this);
			if (tmpVal != ArgObject.EVALUATION_UNKNOWN) {
				statements.put(st, new Integer(tmpVal));
			}
		}
		return tmpVal;
	}
	/**
	 * Returns the conditional within this Theory with the given antecedent
	 * and consequent
	 * @param antecedent Statement antecedent to the Conditional
	 * @param consequent Statement consequent to the Conditional
	 * @return
	 * null if the object is not in the theory, or the first Conditional with
	 * the given specifications found in the Theory
	 */
	public Conditional getConditional(ArgObject antecedent, ArgObject consequent) {
		int i, newVal;
		boolean didChange;
		Enumeration keys;
		ArgObject curStatement;
		
		keys = statements.keys();
		while (keys.hasMoreElements()) {
			curStatement = (ArgObject)keys.nextElement();
			if (curStatement instanceof Conditional) {
				if (((Conditional)curStatement).getAntecedent() == antecedent && ((Conditional)curStatement).getConsequent() == consequent) {
					return (Conditional)curStatement;
				}
			}
		}
		return null;
	}

	/**
	 * Retrieves the valuation of a given statement, if it exists
	 * within the theory
	 * @param st statement for with to retrieve the valuation 
	 * @return 
	 * Statement.EVALUATION_UNASSIGNED if the statement was unassigned or not present,
	 * Statement.EVALUATION_UNDEFEATED if the statement is justified,
	 * Statement.EVALUATION_DEFEATED if the statement is defeated,
	 * Statement.EVALUATION_UNKNOWN if there is conflicting evidence
	 */
	public int getValue(ArgObject st) {
		int i;
		if (st == null) return ArgObject.EVALUATION_UNASSIGNED;
		Integer tmp;
		tmp = (Integer)statements.get(st);
		if (tmp != null) {
			return tmp.intValue();
		}
		return ArgObject.EVALUATION_UNASSIGNED;
	}
	
	/**
	 * Sets the value of st within this Theory to a new value.
	 * Does nothing if st is not in this Theory.
	 * @param st Statement to change value
	 * @param value New value of the statement
	 */
	protected void setValue(ArgObject st, int value) {
		if (statements.containsKey(st)) {
			statements.put(st,new Integer(value));
		}
	}
	
	public String toString() {
		int i;
		String rt = "";
		ArgObject [] st = getStatements();
		
		if(statements != null) {
			for(i =0; i < st.length; i++) {
				rt += st[i].toString() + "\n";
			}
		}
		
		Hashtable vars = Variable.getTable();
		Enumeration e1 = vars.elements();
		Enumeration e2 = vars.keys();
		
		rt+="\nVariables:\n";
		
		while (e1.hasMoreElements() && e2.hasMoreElements())
		{
			Object a =e1.nextElement();
			Object b = e2.nextElement();
			
			rt+= (String)b + "=" + (String)a + "\n";
		}
		
		return rt;
	}

	public ArgObject[] giveRootStatements()
	{
		ArgObject[] statements = getStatements();
		ArgObject[] rt = null;
		
		for(int i =0; i < statements.length; i++)
		{
			if(!(statements[i] instanceof Conditional))
			{
				//a Conditional cannot be one of the roots!
				if(isRoot(statements[i],statements)) 
				{
					//add this statement to the roots
					if(rt == null) 
					{
						rt = new ArgObject[1];
						rt[0]=statements[i];
					}
					else
					{
						ArgObject[] dum = new ArgObject[rt.length + 1];
						for(int j=0; j < rt.length; j++)
							dum[j] = rt[j];
						dum[rt.length] = statements[i];
						rt = dum;
					}
				}
			}
		}
		return rt;		
	}
	
	private boolean isRoot(ArgObject ao1, ArgObject[] statements)
	{
		if (ao1 instanceof Conditional || ao1 instanceof Negation) return false;
		boolean found= false;
		int i =0;
		while(!found && i < statements.length )
		{
			if(statements[i] instanceof Conditional)
			{
				//een root komt niet voor in een antecedent van een conditional!
				Conditional c1 = (Conditional)statements[i];
				if(c1.containsAntecedent(ao1)) {
					return false;
				}
			} else if (statements[i] instanceof Conjunct) {
				if (((Conjunct)statements[i]).contains(ao1)) {
					return false;
				}
			}
			i++;
		}
		return true;
	}
	
	
	public ArgObject makeTree(ArgObject s1)
	{	
		/*ArgObject[] statements = getStatements();
		if(statements != null)
		{
			for(int i =0; i < statements.length; i++)
			{
				//delete all (old) 'tree-links'
				statements[i].removeAllAttackers();
				statements[i].removeAllSupporters();
			}	
			makeTreeFor(s1,statements, new Vector());
		}*/
		return s1;
	}
	
/*	private void makeTreeFor(ArgObject s1, ArgObject[] stats1, Vector done)
	{
		//String className = s1.getClass().getSimpleName();
		done.add(s1);
		
		for(int i =0; i < stats1.length; i++)
		{
			if(stats1[i].getClass().getSimpleName().equals("Conditional"))
			{
				//System.out.println("Conditional found");
				Conditional c1 = (Conditional)stats1[i];
				ArgObject st1 = c1.getConsequent();
				if(!done.contains(c1)) makeTreeFor(c1,stats1,done);
				if(st1.getClass().getSimpleName().equals("Negation"))
				{
					//System.out.println("Negation found");
					Negation n1 = (Negation)st1;										
					if(n1.originalStatement == s1)
					{
						//System.out.println("FOUND!");	
						s1.addAttacker(c1);
						for(int j =0; j < c1.getAntecedents().length; j++)
						{
							if(!done.contains(c1.getAntecedents()[j]))
								makeTreeFor(c1.getAntecedents()[j],stats1,done);
						}
					}
				}
				else
				{
					//System.out.println("Not negation found");
					if(st1 == s1)
					{
						s1.addSupporter(c1);
						for(int j =0; j < c1.getAntecedents().length; j++)
						{
							if(!done.contains(c1.getAntecedents()[j]))
								makeTreeFor(c1.getAntecedents()[j],stats1,done);
						}
					}
				}
			}
		}
	}*/	

}
